﻿#region Copyright information
// 
// Copyright © 2005-2013 Yongkee Cho. All rights reserved.
// 
// This code is a part of the Biological Object Library and governed under the terms of the
// GNU Lesser General  Public License (LGPL) version 2.1 which accompanies this distribution.
// For more information on the LGPL, please visit http://bol.codeplex.com/license.
// 
// - Filename: ArrayGenomeBase.cs
// - Author: Yongkee Cho
// - Email: yongkeecho@gmail.com
// - Date Created: 2012-12-08 8:26 AM
// - Last Modified: 2013-01-25 3:59 PM
// 
#endregion
using System;
using System.Collections.Generic;
using System.Linq;

namespace BOL.Algorithms.Optimization.EvolutionaryAlgorithms
{
    public abstract class ArrayGenomeBase<TGene> : GenomeBase<TGene>, IArrayGenome<TGene>
    {
        #region Public properties

        public TGene[] Sequence { get; set; }

        public int Length { get { return Sequence.Length; } }

        #endregion

        #region Constructors

        protected ArrayGenomeBase(Random random, TGene[] sequence)
            : base(random)
        {
            Sequence = sequence;
        }

        protected ArrayGenomeBase(Random random, int length)
            : this(random, new TGene[length]) { }

        #endregion

        #region Public methods

        private void Crossover(IArrayGenome<TGene> other)
        {
            var crossoverPoint = Random.Next(Math.Min(Length, other.Length));

            var dadGenome1 = Sequence.Take(crossoverPoint);
            var dadGenome2 = Sequence.Skip(crossoverPoint).Take(Length - crossoverPoint);
            var momGenome1 = other.Sequence.Take(crossoverPoint);
            var momGenome2 = other.Sequence.Skip(crossoverPoint).Take(other.Length - crossoverPoint);

            Sequence = dadGenome1.Concat(momGenome2).ToArray();
            other.Sequence = momGenome1.Concat(dadGenome2).ToArray();
        }

        public override void Crossover(IGenome<TGene> other)
        {
            if (other is IArrayGenome<TGene>)
                Crossover(other as IArrayGenome<TGene>);
            else
                throw new InvalidCastException("Other is not an IArrayGenome<TGene> type.");
        }

        #endregion

        #region IEquatable<IArrayGenome<TGene>> implementation

        public bool Equals(IArrayGenome<TGene> other)
        {
            return Length == other.Length && Sequence.SequenceEqual(other.Sequence, EqualityComparer<TGene>.Default);
        }

        #endregion

        #region Object overriden

        public override int GetHashCode()
        {
            unchecked
            {
                const int p = 16777619;
                var hash = (int)2166136261;

                for (var i = 0; i < Length; i++)
                    hash = (hash ^ Sequence[i].GetHashCode()) * p;

                hash += hash << 13;
                hash ^= hash >> 7;
                hash += hash << 3;
                hash ^= hash >> 17;
                hash += hash << 5;

                return hash;
            }
        }

        public override bool Equals(object other)
        {
            if (other == null)
                throw new ArgumentNullException("other");

            if (!(other is IArrayGenome<TGene>))
                throw new InvalidCastException("The 'other' argument is not a IArrayGenome<TGene> object.");

            return Equals(other as IArrayGenome<TGene>);
        }

        public override string ToString()
        {
            return String.Format("({0})", String.Join(", ", Sequence));
        }

        #endregion
    }

    public abstract class LamarckianArrayGenomeBase<TGene, TPhenome> : ArrayGenomeBase<TGene>, ILamarckianGenome<TGene, TPhenome>
    {
        protected LamarckianArrayGenomeBase(Random random, TGene[] sequence)
            : base(random, sequence)
        {
        }

        public abstract void Optimize();
    }
}
